///================================================================///
/// rsvp-network.click
///
/// This script implements a small IP network inside click. Several
/// compound elements are used to create the different network entities.
///
/// This script is part of the assignment of the 
/// course 'Telecommunicatiesystemen 2007-2008'. The assignment explains
/// in more detail the network architecture.
///
/// Authors: Bart Braem & Michael Voorhaen
///================================================================///

///===========================================================================///
/// An IP router with 1 interface.
elementclass IPRouter1int 
{
$addr_info, $gateway
|

	switch :: QosSwitch;	
	qosManager :: QosManager($addr_info:ip);
	myIpClassifier :: IPClassifier(udp port 1001, udp port 1000);
	ps :: PrioSched();
	q1 :: Queue;
	q2 :: Queue;
	q3 :: Queue;
	setTos :: SetTos();
	
	rt :: StaticIPLookup(
		$addr_info:ip/32 0,
		$addr_info:ipnet 1,
		0.0.0.0/0 $gateway 1
	);

	ip :: Strip(14)
	-> CheckIPHeader() -> switch;
	
	switch[0] -> qosManager
	switch[1] -> rt;
	
	qosManager[0] -> q1 -> [0]ps;
	qosManager[1] -> myIpClassifier;
	myIpClassifier[0] -> setTos -> q2 -> [1]ps;
	myIpClassifier[1] -> q3 -> [2]ps;
	ps -> Unqueue -> rt;
	
	// ARP responses are copied to each ARPQuerier and the host.
	arpt :: Tee(1);
	
	// Input and output paths for eth0
	c0 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[0] -> HostEtherFilter($addr_info:eth) -> c0;
	c0[0] -> ar0 :: ARPResponder($addr_info) -> [0]output;
	arpq0 :: ARPQuerier($addr_info, $addr_info) -> [0]output;
	c0[1] -> arpt;
	arpt[0] -> [1]arpq0;
	c0[2] -> Paint(1) -> ip;
		
	// Local delivery
	rt[0] -> [1]output;
	
	// Forwarding path for eth0
	rt[1] -> DropBroadcasts
	-> gio0 :: IPGWOptions($addr_info)
	-> FixIPSrc($addr_info)
	-> dt0 :: DecIPTTL
	-> fr0 :: IPFragmenter(1500)
	-> [0]arpq0;
	dt0[1] -> ICMPError($addr_info, timeexceeded) -> rt;
	fr0[1] -> ICMPError($addr_info, unreachable, needfrag) -> rt;
	gio0[1] -> ICMPError($addr_info, parameterproblem) -> rt;
	
}

//===========================================================================///
/// An IP router with 2 interfaces.
elementclass IPRouter2int 
{
$private_address, $public_address, $default_gateway
|

	reserver :: Reserver;
	switch :: QosSwitch;
	ps :: PrioSched();
	q1 :: Queue;
	q2 :: Queue;
	q3 :: Queue;
	tosClassifier :: TosClassifier();
	
	rt :: StaticIPLookup(
		$private_address:ip/32 0,
		$public_address:ip/32 0,
		$private_address:ipnet 1,
		$public_address:ipnet 2,
		0.0.0.0/0 $default_gateway 2);

	ip :: Strip(14)
	-> CheckIPHeader(DETAILS true) -> switch;
	
	switch[0] -> reserver;
	switch[1] -> rt;
	
	reserver[0] -> q1 -> [0]ps;
	reserver[1] -> tosClassifier;
	tosClassifier[0] -> q2 -> [1]ps;
	tosClassifier[1] -> q3 -> [2]ps;
	
	ps -> Unqueue -> rt;
		
	// ARP responses are copied to each ARPQuerier and the host.
	arpt :: Tee(2);
	
	// Input and output paths for eth0
	c0 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[0] -> c0;
	c0[0] -> ar0 :: ARPResponder($private_address) -> [0]output;
	arpq0 :: ARPQuerier($private_address) -> [0]output;
	c0[1] -> arpt;
	arpt[0] -> [1]arpq0;
	c0[2] -> Paint(1) -> ip;
	
	// Input and output paths for eth1
	c1 :: Classifier(12/0806 20/0001, 12/0806 20/0002, -);
	input[1] -> c1;
	c1[0] -> ar1 :: ARPResponder($public_address) -> [1]output;
	arpq1 :: ARPQuerier($public_address) -> [1]output;
	c1[1] -> arpt;
	arpt[1] -> [1]arpq1;
	c1[2] -> Paint(2) -> ip;
	
	// Local delivery
	rt[0] -> [2]output; 
	
	// Forwarding path for eth0
	rt[1] -> DropBroadcasts
	-> cp0 :: PaintTee(1)
	-> gio0 :: IPGWOptions($private_address)
	-> FixIPSrc($private_address)
	-> dt0 :: DecIPTTL
	-> fr0 :: IPFragmenter(1500)
	-> [0]arpq0;
	dt0[1] -> ICMPError($private_address, timeexceeded) -> rt;
	fr0[1] -> ICMPError($private_address, unreachable, needfrag) -> rt;
	gio0[1] -> ICMPError($private_address, parameterproblem) -> rt;
	cp0[1] -> ICMPError($private_address, redirect, host) -> rt;
	
	// Forwarding path for eth1
	rt[2] -> DropBroadcasts
	-> cp1 :: PaintTee(2)
	-> gio1 :: IPGWOptions($public_address)
	-> FixIPSrc($public_address)
	-> dt1 :: DecIPTTL
	-> fr1 :: IPFragmenter(1500)
	-> [0]arpq1;
	dt1[1] -> ICMPError($public_address, timeexceeded) -> rt;
	fr1[1] -> ICMPError($public_address, unreachable, needfrag) -> rt;
	gio1[1] -> ICMPError($public_address, parameterproblem) -> rt;
	cp1[1] -> ICMPError($public_address, redirect, host) -> rt;

}

//===========================================================================///
/// A bandwidth limited medium
elementclass BandWidthLimited
{
$maxrate
|

	myIpClassifier :: IPClassifier(RSVP, -);

	// Split bandwidth of incoming data
	input[0] -> myIpClassifier; 
	
	myIpClassifier[0] -> [0]output;
	myIpClassifier[1] -> rate_limit::BandwidthRatedSplitter($maxrate);
	
	// Data at a rate lower than the limit
	rate_limit[0] -> [0]output;
	
	// Data at a rate higher than the limit
	rate_limit[1] -> Discard;
}

///===========================================================================///
/// Definitions of the different hosts and related address information.

/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/// !!!!!!! DO NOT EDIT BELOW THIS LINE: Any changes made below, will be replaced prior to the project defense !!!!!!!!
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
internet :: ListenEtherSwitch;

AddressInfo(router_network1_private_address 192.168.1.254/24 00:50:BA:85:84:B1);
AddressInfo(router_network1_public_address 10.0.1.254/24 00:50:BA:85:84:B2);

AddressInfo(router_network2_private_address 192.168.2.254/24 00:50:BA:85:84:C1);
AddressInfo(router_network2_public_address 10.0.2.254/24 00:50:BA:85:84:C2);

router1 :: IPRouter2int(router_network1_private_address, router_network1_public_address, router_network2_public_address);

router2 :: IPRouter2int(router_network2_private_address, router_network2_public_address, router_network1_public_address);

client_network1 :: ListenEtherSwitch;
AddressInfo(client11_address 192.168.1.1/24 00:50:BA:85:84:D1);
client11 :: IPRouter1int(client11_address, router_network1_private_address);

client_network2 :: ListenEtherSwitch;
AddressInfo(client21_address 192.168.2.1/24 00:50:BA:85:84:E1);
client21 :: IPRouter1int(client21_address, router_network2_private_address);

client11[0]
	-> rate_limit_client1_network1::BandWidthLimited(1000kbps)
	-> [0]client_network1[0]
	-> [0]client11

client21[0]
	-> rate_limit_client2_network2::BandWidthLimited(1000kbps)
	-> [0]client_network2[0]
	-> [0]client21

router1[0]
	-> rate_limit_router1_network1::BandWidthLimited(1000kbps)
	-> [1]client_network1[1]
	-> [0]router1

router2[0]
	-> rate_limit_router2_network2::BandWidthLimited(1000kbps)
	-> [1]client_network2[1]
	-> [0]router2

router1[1]
 	-> rate_limit_router1::BandWidthLimited(1000kbps)
	-> [0]internet[0]
	-> [1]router1

router2[1]
	->  rate_limit_router2::BandWidthLimited(1000kbps)
	-> [1]internet[1]
	-> [1]router2

client_network1[2]
	-> ToDump("client_network1.dump");

client_network2[2]
	-> ToDump("client_network2.dump");

internet[2]
	-> ToDump("internet.dump");

/// let the corresponding node send a ping
RatedSource(DATASIZE 83, RATE 100)
	-> DynamicUDPIPEncap(client11_address:ip, 1234, client21_address:ip, 1001) 
	-> EtherEncap(0x0800, client11_address:eth, client11_address:eth) /// The MAC addresses here shoudl be from the client11  to get past the HostEtherFilter. This way we can reuse the input from the network for the applications.
	//-> IPPrint("client11 -- sent QoS packet")
	-> counterSrcQoS::AverageCounter
	-> [0]client11

RatedSource(DATASIZE 83, RATE 1000)
	-> DynamicUDPIPEncap(client11_address:ip, 1234, client21_address:ip, 1000) 
	-> EtherEncap(0x0800, client11_address:eth, client11_address:eth) /// The MAC addresses here shoudl be from the client11  to get past the HostEtherFilter. This way we can reuse the input from the network for the applications.
	//-> IPPrint("client11 -- sent BE packet")
	-> counterSrcBE::AverageCounter
	-> [0]client11

/// packets destined for the mobile node
client11[1]
	//-> IPPrint("client11 -- received packet") 
	-> Discard

/// packets destined for the mobile node
client21[1]
	-> Unstrip(14)
	-> MarkIPHeader(14)
	-> ipClassifier :: IPClassifier ( udp port 1000, udp port 1001 )

ipClassifier[0]
	-> counterDstBE::AverageCounter
	-> ToDump(be.dump)
	//-> IPPrint("client21 -- received BE packet") 
	-> Discard

ipClassifier[1]
	-> counterDstQoS::AverageCounter
	-> ToDump(qos.dump)
	//-> IPPrint("client21 -- received QoS packet") 
 	-> Discard

/// packets destined for the mobile node
router1[2]
	//-> IPPrint("router1 -- received UDP packet") 
	-> Discard

/// packets destined for the mobile node
router2[2]
	//-> IPPrint("router2 -- received UDP packet") 
	-> Discard